#pragma once

#include "../DefinesCore.hpp"
#include <deque>
#include <mutex>

CYBERTRON_BEGIN

template <typename T>
class ConcurrentQueue
{
public:
	ConcurrentQueue()
	{
		mSize = 0;
		mHead = nullptr;
		mTail = nullptr;
	}

	~ConcurrentQueue()
	{
		clear();
	}

	void clear()
	{
		std::unique_lock<std::mutex> lock(mMutex);
		while (mHead != nullptr)
		{
			Item *item = mHead;
			mHead = mHead->next;
			freeItem(item);
		}
		mTail = nullptr;
		mSize = 0;
	}

	bool empty() const
	{
		std::unique_lock<std::mutex> lock(mMutex);
		return (mSize == 0);
	}

	size_t size() const
	{
		std::unique_lock<std::mutex> lock(mMutex);
		return mSize;
	}

	size_t push_back(const T& val)
	{
		std::unique_lock<std::mutex> lock(mMutex);
		Item* item = new Item();
		item->data = new T(val);
		if (mHead == nullptr)// First element;
			mHead = item;
		else
			mTail->next = item;
		mTail = item;
		mSize++;
		return mSize;
	}

	bool pop_front(T&& result)
	{
		std::unique_lock<std::mutex> lock(mMutex);
		if (mHead == nullptr)
		{
			return false;
		}
		Item *item = mHead;
		mHead = mHead->next;
		--mSize;
		result = std::move(*(item->data));
		freeItem(item);
		return true;
	}

	bool pop_swap_front(T& result)
	{
		std::unique_lock<std::mutex> lock(mMutex);
		if (mHead == nullptr)
		{
			return false;
		}
		Item *item = mHead;
		mHead = mHead->next;
		--mSize;
		result.swap(*(item->data));
		freeItem(item);
		return true;
	}

private:
	uint16_t mSize;
	mutable std::mutex mMutex;
	struct Item
	{
		T* data = nullptr;
		Item *next = nullptr;
	};
	Item* mHead;
	Item* mTail;

	inline void freeItem(Item *item)
	{
		delete item->data;
		delete item;
	}
};

CYBERTRON_END
